﻿var net = require('net');
var url = require('url');
//var program = require('commander');
var http = require('http');
var util = require('util');
var test_server = require('./test_server_8053.js');

function range(val) {
    return val.split('..').map(Number);
}

function list(val) {
    return val.split(',')
}



function calculate_head_len(protocol) {
    var headlen = 0;
    if (protocol.start != null)
        headlen += protocol.start.len;

    headlen += protocol.deviceid.len;
    headlen += protocol.contentlen.len;
    headlen += protocol.cmd.len;
    if (protocol.headfields != null)
        headlen += protocol.headfields.len;
    //console.log("the headlen is", headlen);
    return headlen;
}

function calculate_tail_len(protocol) {
    var taillen = 0;
    if (protocol.crc != null)
        taillen += protocol.crc.len; //这个在尾部
    if (protocol.end != null)
        taillen += protocol.end
    return taillen;
}

function cal_max_body_len(protocol) {
    var max = calculate_head_len(protocol);
    max += calculate_tail_len(protocol);
    if (protocol.contentlen.len == 1)
        max += 256;
    else if (protocol.contentlen.len == 2)
        max += 65536;
    else
        max += 65536 * 2; //最多两个64k分片长度
    return max;
}

var device_sock_map = {};
var protocol_sc_g = null;

function ProcessTcpServer(protocol, protocol_sc, port, callback) {
    if (protocol.bodyfields == null) {
        console.log("bodyfields is null");
        return;
    }
    // var options = parseurl(urlpath);
    var protocol_map = new Map();
    for (var i = 0; i < protocol.bodyfields.length; i++) {
        var cmd = protocol.bodyfields[i].cmd;
        protocol_map.set(cmd, protocol.bodyfields[i].field);
    }

    protocol_sc_g = protocol_sc;

    //聚合所有客户端  
    //var sockets = [];
    if (protocol == null || protocol.cmd == null || protocol.contentlen == null)
        return;
    if (protocol.deviceid == null)
        return;
    var hlen = calculate_head_len(protocol);
   // console.log("head len is", hlen);
    var tlen = calculate_tail_len(protocol);
   // console.log("the tail len is ", tlen);
    var max_body_len = cal_max_body_len(protocol);
   // console.log("the maxbody is ", max_body_len);

    var server = net.createServer();
    //接受新的客户端连接  
    server.on('connection', function (socket) {
        var config_read_sensor = {};
        var port = socket.remotePort;
        var addr = socket.remoteAddress;
        console.log('CONNECTED: ' + socket.remoteAddress + ':' + socket.remotePort);
        //sockets.push(socket);
        //缓冲区 512字节
        const buf = Buffer.allocUnsafe(max_body_len);
        //缓存中的数据长度
        var buf_datasize = 0;
        //69 00 01 00 01 04 08 46 7A 0B E8 41 F1 00 00 B2 87 16      // 压力16000  +  温度30.1 
        //处理子协议头部
        function process_bele_field_content_child_1(buff, fields, pos) {
            var obj = {};
            obj.head = {};
            var spos = pos;
            // console.log("process the data of child_1 :", spos);
            var total_len = 0;
            //var len = 0;
            var bitlen = 0; for (var i = 0; i < fields.length; i++) {
                var type = fields[i].type;
                var ftype = fields[i].ftype;
                var flen = fields[i].len;
                var name = fields[i].name;
                var len = 0;
                var value = null;

                switch (type) {
                    case "int8":
                        len += 1;
                        value = buff[spos];
                        break;
                    case "bit": {
                        bitlen += flen;
                        if (bitlen == 8) {
                            len += 1;
                            bitlen = 0;
                        }
                        var bitoffset = parseInt(ftype);//位偏移
                        var offset = spos; //字节偏移
                        var bv = buff[offset];
                        //var t1 = (Math.pow(2, 9 - bitoffset) - 1);
                        //var t2 = (9-bitoffset-flen );
                        //console.log("bv,t1", bv, t1, t2);
                        value = bv & (Math.pow(2, 9 - bitoffset) - 1);
                        value = value >> (9 - bitoffset - flen);

                        //return value;
                    }
                        break;
                    case "int16":
                        value = buff.readInt16BE(spos);
                        len += 2;
                        break;
                    case "int32":
                        value = buff.readInt32BE(spos);
                        len += 4;
                        break;
                }
                if (bitlen == 0)
                    spos += len;
                total_len += len;
                obj.head[name] = value;
            }
            obj.headlen = total_len;
            //console.log("the obj is :", obj);
            return obj;
        }
        //处理数据
        function process_bele_field_content_child_2(buff, fieldobj, pos) {
            var k = 1;
            var num = 0;
            var len = 0;
            var point = 0;
            var spos = pos;
            var value = [];
            ///console.log(fieldobj);
            for (var key in fieldobj.head) {
                if (k == 1)
                    num = parseInt(fieldobj.head[key]);
                else if (k == 2) {
                    len = parseInt(fieldobj.head[key]);
                    // break;
                }
                else if (k == 3) {
                    point = parseInt(fieldobj.head[key]);
                }
                //else if( k == 4)
                k++;
            }
            var tlen = num * len;
            //console.log("tlen is: ", tlen);
            if (tlen <= 0) {
                console.log("error len");
                return 0;
            }
            else {

                for (var i = 0; i < num; i++) {
                    if (len == 1) {
                        value.push(buff[spos]);
                    }
                    else if (len == 2) {
                        value.push(buff.readUInt16BE(spos));
                    }
                    else if (len == 4) {
                        if (point > 0) { //如果小数位数大于0,说明是浮点数
                            value.push(buff.readFloatBE(spos));
                        }
                        else {
                            //否则就是整数
                            value.push(buff.readUInt32BE(spos));
                        }
                    }
                    spos += len;
                }//for end
            }//else
            fieldobj.value = value;
            //返回处理数据的长度
            return tlen;
        }

        //处理子协议函数 ftype == leftcycle 剩余字节循环
        function process_bele_field_content_child(buff, field_child, pos, leftdatalen) {
            //buf长度
            var len = buff.length;
            var left_len = leftdatalen; //剩余所有的字节数目

            var spos = pos;
            var data = [];

            while (left_len > 0) {
                //                console.log("the left_len is :", left_len,len , pos);
                var fieldobj = process_bele_field_content_child_1(buff, field_child, spos);
                var headlen = fieldobj.headlen;
                spos += headlen; //处理头部长度
                var contentlen = process_bele_field_content_child_2(buff, fieldobj, spos);
                if (contentlen == 0)
                    return data;
                spos += contentlen;

                left_len -= contentlen;
                left_len -= headlen;
                data.push(fieldobj);
            }
            return data;
        }
        //增加处理子协议,leftdatalen 剩余需要分析的字节数
        function get_obj_field_value(buff, fieldobj, pos, leftdatalen) {
            var type = fieldobj.type;
            var len = fieldobj.len;
            var ftype = fieldobj.ftype;
            var name = fieldobj.name;
            var value = null;
            if (type == "float") {
                if (ftype == "bige")
                    value = buff.readFloatBE(pos);
                else
                    value = buff.readFloatLE(pos);
            }
            else if (type == "double") {
                if (ftype == "bige")
                    value = buff.readDoubleBE(pos);
                else
                    value = buff.readDoubleLE(pos);
            }
            else if (type == "int16") {
                if (ftype == "bige")
                    value = buff.readInt16BE(pos);
                else
                    value = buff.readInt16LE(pos);
            }
            else if (type == "uint16") {
                if (ftype == "bige")
                    value = buff.readUInt16BE(pos);
                else
                    value = buff.readUInt16LE(pos);
            }
            else if (type == "int32") {
                if (ftype == "bige")
                    value = buff.readInt32BE(pos);
                else
                    value = buff.readInt32LE(pos);
            }
            else if (type == "uint32") {
                if (ftype == "bige")
                    value = buff.readUInt32BE(pos);
                else
                    value = buff.readUInt32LE(pos);
            }
            else if (type == "uint8") {
                value = buff.readUInt8(pos);
            }
            else if (type == "int8") {
                value = buff.readInt8(pos);
            }
            else if (type == "string") {
                value = buff.toString('utf8', pos, pos + len);
            }
            else if (type == "time6") {
                var timestr = "";
                value1 = buff[pos];
                value1 += 2000;
                timestr += value1;
                timestr += "-";
                timestr += buff[pos + 1];//月
                timestr += "-";
                timestr += buff[pos + 2];//日
                timestr += " ";
                timestr += buff[pos + 3];
                timestr += ":";
                timestr += buff[pos + 4];
                timestr += ":";
                timestr += buff[pos + 5];
                return timestr;
            }
            else if (type == "protocol" && ftype == "leftcycle") {//子协议处理

                // const realdata = Buffer.allocUnsafe(datalen);
                // buff.copy(realdata, 0, hlen, hlen + datalen);
                if (fieldobj.field_child != null)
                    value = process_bele_field_content_child(buff, fieldobj.field_child, pos, leftdatalen);
                else
                    value = null;
                //console.log(value);
            }
            return value;
        }


        ///fields 是bodyfields headfields 就是头部field
        //datalen : 剩余需要分析的数据长度
        function process_bele_field_content(fields, buff, startpos, cmd, datalen) {
            //var bodypos = ;
            //位置加上头部字节
            var pos = startpos + hlen;
            var retobj = {};
            retobj.field = {};
            var buflen = buff.length;
            var leftdatalen = datalen;
            for (var i = 0; i < fields.length; i++) {
                // if(pos + datalen >= )
                var value = get_obj_field_value(buff, fields[i], pos, leftdatalen);
                if (value != null) {
                    var name = fields[i].name;
                    if (fields[i].type == "protocol" && fields[i].ftype == "leftcycle")
                        retobj[name] = value;
                    else
                        retobj.field[name] = value;
                }
                pos += fields[i].len;
                //计算剩余的字节数目
                leftdatalen -= fields[i].len;

            }//for end

            retobj.cmd = cmd;
            if (protocol.headfields == null || protocol.headfields.field == null)
                return retobj;
            var flength = protocol.headfields.field.length;
            if (flength == 0)
                return retobj;
            var h_field = protocol.headfields.field;
            //console.log("jjj",h_field,h_field.length);
            //得到第一个偏移量
            var first_offset = h_field[0].offset;
            // console.log("first_offset", first_offset);
            ////////////////////////////////////
            ////////
            //继续写
            pos = startpos + first_offset - 1;
            retobj.headfield = {};// [];
            //计算

            for (var j = 0; j < h_field.length; j++) {
                // console.log("h_field",h_field[j]);
                var value = get_obj_field_value(buff, h_field[j], pos);
                if (value != null) {
                    var name = h_field[j].name;
                    //var headobj = {};
                    //headobj[name] = value;
                    //headobj.name = h_field[j].name;
                    //headobj.value = value;
                    retobj.headfield[name] = value; //.push(headobj);
                }
                pos += h_field[j].len;
            }
            return retobj;
        }


        function process_packet_1(buff, startpos, datalen) {
            //  console.log("the datalen is %d", datalen);
            var cmdoffset = protocol.cmd.offset + startpos;
            var cmdlen = protocol.cmd.len;
            var cmd = null;
            if (cmdlen == 1)
                cmd = buff[cmdoffset - 1];
            else if (cmdlen == 2)
                cmd = buff.readInt16BE(cmdoffset - 1);
            else if (cmdlen == 4)
                cmd = buff.readInt32BE(cmdoffset - 1);
            //console.log("original data:", buff);
            // var temp = util.format("the protocol cmd is %d", cmd);
            // console.log(temp);
            var str_deviceid = "";
            var idpos = protocol.deviceid.offset - 1 + startpos;

            str_deviceid = buff.toString('hex', idpos, idpos + protocol.deviceid.len);
            socket.deviceid = str_deviceid;
            device_sock_map[str_deviceid] = socket;
            // 通知父进程该设备号在线
            process_send({flag:2, deviceid:str_deviceid, status:1});
            //temp = util.format("the deviceid is %s", str_deviceid);
            //console.log(temp);

            //从命令表中获取字段,如果字段不是固定的,需要使用外部特殊处理
            var fields = protocol_map.get(cmd);
            if (fields == null) {
                temp = util.format("---error! the cmd is %d,but in protocol not find this command---", cmd);
                console.log(temp);
                //return;
            }
            else {
                //计算字段需要的长度
                var clen = 0;
                for (var i = 0; i < fields.length; i++) {
                    clen += fields[i].len;
                }
                //这里可能不正确,涉及到数据字段是否包含二次协议
                if (datalen != clen) {
                    //console.log("---maybe error bodylen %d not ok,maybe not process---", datalen);
                }

                //从协议中获取所有字段的值并且生成对象
                //datalen 需要分析的数据长度
                var retobj = process_bele_field_content(fields, buff, startpos, cmd, datalen);
                //for (var x = 0; x < retobj.protocol.length; x++) {
                //    console.log("xyz_head:", retobj.protocol[x].head);
                //    console.log("xyz_value:", retobj.protocol[x].value);
                //}
                //post 回去

                if (callback != null && retobj != null) {
                    retobj.deviceid = str_deviceid; //deviceid 赋值
                    callback(retobj);
                }
                // console.log("receive retobj", retobj);

                //特殊处理,这一部分这样计算,如果处理的字段没有结束,说明有二次协议包含在数据字段中
                /*
                if ((clen < datalen) && cmd == 0x0202) { 
                    var llen = datalen - clen;

                    const realdata = Buffer.allocUnsafe(datalen);
                    buff.copy(realdata, 0, hlen, hlen + datalen);
                    process_cmd_0202(realdata);

                }
                */

            }


        }

        function process_packet_compelete(buff) {
            //  console.log("process_packet_compelete %d", buf_datasize);
            var startpos = 0;
            //var body_len = 0;
            var packet_len = 0;
            while (true) {
                if (buf_datasize >= hlen) {
                    //考虑是1个字节，2个字节，4个字节，其他不考虑
                    var boffset = protocol.contentlen.offset;
                    var boffset_l = protocol.contentlen.len;
                    if (boffset_l == 2)
                        packet_len = buff.readInt16BE(boffset - 1);
                    else if (boffset_l == 4)
                        packet_len = buff.readInt32BE(boffset - 1);
                    else
                        packet_len = buff[boffset - 1];
                    // console.log("packet_len %d", packet_len);
                }
                else {
                    if (buf_datasize == 0)
                        return 0;
                    return startpos;
                }
                //packet_len = hlen + body_len+ tlen;
                //packet_len = body_len;
                //console.log("packet len is %d", packet_len);
                var datalen = packet_len;
                if (protocol.contentlen.includeht != null) {
                    if (protocol.contentlen.includeht > 0) //如果包含头部和尾部
                        datalen = packet_len - hlen - tlen; //减去后得到数据长度
                }
                var s = util.format("the data len is %d", datalen);
                //console.log(s);
                if (buf_datasize >= (packet_len)) {
                    process_packet_1(buff, startpos, datalen);
                    buf_datasize -= packet_len;
                    startpos += packet_len;
                }
                else {
                    if (buf_datasize == 0)
                        return 0;
                    return startpos;
                }
            }
        }

        socket.on('data', function (data) {
            console.log('receive data',data);
            //把数据拷贝到buf之后
            //nodejs.org doc : buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
            data.copy(buf, buf_datasize);
            buf_datasize += data.length;
            //  console.log("now recv length " + buf_datasize);
            if (buf_datasize < hlen) {//不够包头的长度，继续接收
                return;
            }
            var startpos = process_packet_compelete(buf);
            if (startpos > 0) {
                buf.copy(buf, 0, startpos, startpos + buf_datasize);
            }

        });
        //删除被关闭的连接  
        socket.on('close', function () {
            console.log('connection closed');
            if (socket.deviceid != null){
                // 通知父进程该设备号不在线
                process_send({flag:2, deviceid:socket.deviceid, status:0})
                delete device_sock_map[socket.deviceid];
            }
           // var index = sockets.indexOf(socket);
           // sockets.splice(index, 1);
        });
        socket.on('error', function (err) {
            // console.log("err");
        });
    });

    server.on('close', function () {
        console.log('Server closed');
    });

    if (port == null)
        port = 8051; //出错处理,启动默认端口
    server.listen(port);
    console.log('listening on *:'+ port+' tcpserver');
}
/* 
 功能：子进程接收父进程的消息
  接收到信息后设置，控制协议
*/
// process.on('message', (m) =>{
//     console.log('CHILD got message:', m);
// });
/* 
 功能：子进程向父进程发送信息 
*/
function process_send(data){
    process.send(
          data
    );
}

/*
deviceid:设备号码 
cmd     :命令
value   :值
*/
var protocol_map_sc = null;
function init_sc() {
    protocol_map_sc = new Map();
    for (var j = 0; j < protocol_sc_g.bodyfields.length ; j++) {
        var cmd = protocol_sc_g.bodyfields[j].cmd;
        protocol_map_sc.set(cmd, protocol_sc_g.bodyfields[j].field);
    }
}


function packet_cmd_len(field) {
    var packet_len = 0;
    var body_len = 0;
    for (var key in protocol_sc_g) {
        if (key == "bodyfields") {
            //var blen = 0;
            for (var i = 0; i < field.length; i++) {
                body_len += field[i].len;
            }
            packet_len += body_len;
        }
        else {
            packet_len += protocol_sc_g[key].len;
        }
    }
    return {pack_len:packet_len,body_len:body_len};
}
function packet_cmd_set(buffer,field,cmd,pack_len, body_len,value) {
    var head_len = pack_len -body_len;
    var boffset = 0;
    for (var key in protocol_sc_g) {
        if (key == "bodyfields") { //body
            //boffset--;
            if(boffset<1){
                console.log("error! boffset not ok");
                return ;
            }
            for (var i = 0; i < field.length; i++) {
                var obj = field[i];
                var cur_value = 0;
                if(value[obj.name] != null)
                    cur_value = value[obj.name];
                if (obj.len == 1) {
                    buffer[boffset] = cur_value;
                    boffset++;
                }
                else if (obj.len == 2) {
                    if (obj.ftype == 'bige') {
                        buffer.writeInt16BE(cur_value, boffset);
                    }else{
                        buffer.writeInt16LE(cur_value, boffset);
                    }
                    
                    boffset += 2;
                }
                else if (obj.len == 4) {
                    if (obj.type == "float") {
                        if(obj.ftype == 'bige'){
                           buffer.writeFloatBE(cur_value, boffset);
                        }else{
                           buffer.writeFloatLE(cur_value, boffset);
                        }
                        
                    }
                    else if(obj.type == "int32"){
                        if(obj.ftype == 'bige'){
                           buffer.writeInt32BE(cur_value, boffset);
                        }else{
                           buffer.writeInt32LE(cur_value, boffset); 
                        }
                        
                    }
                    boffset += 4;
                }
            }
            
        }
        else { //head .先写完head
            var obj_h = protocol_sc_g[key];
            var offset = obj_h.offset;
            offset--;
            var len = obj_h.len;
            boffset += len;
            if (key == 'cmd') {
                if (len == 1) {
                    buffer[offset] = cmd;
                }
                else if (len == 2) {
                    buffer.writeInt16BE(cmd, offset);
                }

            }
            else if (key == "contentlen") {
                //= body_len;
                if(obj_h.includeht!=null){
                    if(obj_h.includeht > 0){ //包含头部
                        body_len += head_len;
                    }
                }
                if (len == 1) {
                    buffer[offset] = body_len;
                }
                else if (len == 2) {
                    buffer.writeInt16BE(body_len, offset);
                }
            }

        }
    }
}
function packet_cmd_control(buffer,field,cmd,pack_len, body_len,value) {
    var head_len = pack_len -body_len;
    var boffset = 0;
    for (var key in protocol_sc_g) {
        if (key == "bodyfields") { //body
            //boffset--;
            if(boffset<1){
                console.log("error! boffset not ok");
                return ;
            }
            // 控制包的第一个
            for(var i =0;i< field.length;i++){
                var obj = field[i];
                var cur_value = 0;
                if(obj.values!=null)
                    cur_value = obj.values[value].value;
                if (obj.len == 1) {
                    buffer[boffset] = cur_value;
                    boffset++;
                }
                else if (obj.len == 2) {
                    buffer.writeInt16BE(cur_value, boffset);
                    boffset += 2;
                }
                else if (obj.len == 4) {
                    if (obj.ftype == "float") {
                        buffer.writeFloatBE(cur_value, boffset);
                    }
                    else if(obj.ftype == "int32"){
                        buffer.writeInt32BE(cur_value, boffset);
                    }
                    boffset += 4;
                }
           }
        }
        else { //head .先写完head
            var obj_h = protocol_sc_g[key];
            var offset = obj_h.offset;
            offset--;
            var len = obj_h.len;
            boffset += len;
            if (key == 'cmd') {
                if (len == 1) {
                    buffer[offset] = cmd;
                }
                else if (len == 2) {
                    buffer.writeInt16BE(cmd, offset);
                }

            }
            else if (key == "contentlen") {
                //= body_len;
                if(obj_h.includeht!=null){
                    if(obj_h.includeht > 0){ //包含头部
                        body_len += head_len;
                    }
                }
                if (len == 1) {
                    buffer[offset] = body_len;
                }
                else if (len == 2) {
                    buffer.writeInt16BE(body_len, offset);
                }
            }

        }
    }
}
function isJSON(str) {
    if (typeof str == 'string') {
        try {
            var obj = JSON.parse(str);
            return true;
        } catch(e) {
            return false;
        }
    }else{
        return false;
    }
}
function ProcessTcpClient_cmd(deviceid, cmd, value) {
    if (protocol_map_sc == null)
        init_sc();
    if (isJSON(value) == true) {
        value = JSON.parse(value);
    }
    var field = protocol_map_sc.get(cmd);
    if (field == null) {
        console.log("no this command " + cmd);
        return -1;
    }
    var sock = device_sock_map[deviceid];
    if (sock == null) {
        // console.log("this deviceid " + deviceid + " not online");
        // process_send({statue:-2, deviceid: deviceid, info:'not online'});
        return -2;
    }
    if (protocol_sc_g == null) {
        console.log("error!,no protocol of server 2 client");
        return -3;
    }
    var pblen = packet_cmd_len(field);
    var sendbuffer = Buffer.allocUnsafe(pblen.pack_len);
    if (cmd == 0x01) {//控制命令
        packet_cmd_control(sendbuffer, field, cmd, pblen.pack_len, pblen.body_len, value);
    }
    if (cmd == 0x02) {//设置命令
        packet_cmd_set(sendbuffer, field, cmd, pblen.pack_len, pblen.body_len, value);
    }
    sock.write(sendbuffer);
    return 1;
}

exports.ProcessTcpServer = ProcessTcpServer;
exports.ProcessTcpClient_cmd = ProcessTcpClient_cmd;